#if DIST_VER == 4

#include "cfg.h"
#include "common.h"
#include "dist.h"
#include "formula.h"
#include "sleep_api.h"
#include "vector.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

size_t raw_start_end(size_t begin, size_t end, const seg_info_t *results,
                     size_t len_result, ssize_t last_end,
                     linked_list_t *v4_list) {
  assert((results + begin)->valid);
  assert((results + end)->valid);

  NOTUSED(len_result);

  size_t first = (results + begin)->start_index;
  size_t last = (results + end)->end_index;

  if (last_end >= 0 && (size_t)last_end >= first) {
    sleep_start_end_t *obj = index_item(v4_list, -1, NULL);
    obj->end_index = last;
  } else {
    sleep_start_end_t *obj =
        (sleep_start_end_t *)malloc(sizeof(sleep_start_end_t));
    obj->start_index = first;
    obj->end_index = last;

    append_item(v4_list, obj, NULL);
  }
  return last;
}

static uint8_t _proc_append_cond(const alias_acts_ll_t *last,
                                 uint64_t it_start) {
  return last->end_time != it_start;
}

static uint8_t _find_sleep_range_cond(const seg_info_t *result) {
  return result->hr_avg < SLEEP_HR_AVG_THRESHOLD &&
         result->motion_avg < SLEEP_MOTION_AVG_THRESHOLD &&
         result->steps_diff < SLEEP_STEPS_DIFF_THRESHOLD;
}

static uint8_t _enough(uint64_t start, uint64_t end, uint64_t span) {
  int64_t diff = (int64_t)(end - start);
  return (uint64_t)(ABS(diff)) >= span;
}

static uint8_t _find_sleep_start_end(uint8_t strict_mode,
                                     const smp_hr_t *hr_data, size_t len,
                                     size_t index_start, size_t index_end,
                                     size_t *out_start, size_t *out_end) {
#define ITER4_ALL(start, end, all_mt_th, all_hr_th, all, bot, top, mot_sum,    \
                  avg_mot, fiomz, mzc, mzc_top)                                \
  do {                                                                         \
    for (size_t i = 0; i < len; i++) {                                         \
      const smp_hr_t *x = hr_data + i;                                         \
      if (!(start <= x->ts && x->ts <= end)) {                                 \
        continue;                                                              \
      }                                                                        \
      if ((x->motion > (all_mt_th))) {                                         \
        all = 0;                                                               \
        break;                                                                 \
      }                                                                        \
      top = i;                                                                 \
      if (-1 == bot) {                                                         \
        bot = (ssize_t)i;                                                      \
      }                                                                        \
      if (-1 == fiomz && x->motion <= 5) {                                     \
        fiomz = (ssize_t)i;                                                    \
      }                                                                        \
      if (x->motion <= (mzc_top))                                              \
        mzc++;                                                                 \
      mot_sum += x->motion;                                                    \
    }                                                                          \
    avg_mot = ((double)mot_sum * 1.0 / (double)(top - bot + 1));               \
  } while (0);

#define MATCH_STEPS(all, bot, top, value_avg, enf_cmp, avg_cmp, step_op,       \
                    step_threshold)                                            \
  (bot >= 0 && all && value_avg <= (avg_cmp) &&                                \
   ((hr_data + top)->steps step_op(hr_data + bot)->steps + (step_threshold)))

  ssize_t start_index = -1, end_index = -1;
  size_t find_start = index_start;
  size_t interval_milli = MIN_JUDGMENT_SLEEP_TIME_20 * 60 * 1000;

  while (find_start < index_end) {
    uint64_t start_time = (hr_data + find_start)->ts;
    uint64_t end_time = start_time + interval_milli;

    uint64_t sum_mot = 0;
    uint8_t all = 1;
    ssize_t bot = -1;
    size_t top = 0;
    double avg_mot;

    // 在[start_time, end_time]区间内，并且motion值为0时的索引
    ssize_t first_index_on_motion_zero = -1;
    // 计算mot值为0的总量
    size_t motion_zero_count = 0;

    ITER4_ALL(start_time, end_time,
              strict_mode ? FIND_SLEEP_MOTION_THRESHOLD
                          : FIND_SLEEP_MOTION_THRESHOLD_LAX,
              SLEEP_HR_AVG_THRESHOLD, all, bot, top, sum_mot, avg_mot,
              first_index_on_motion_zero, motion_zero_count,
              strict_mode ? 10 : 20)

    uint8_t check_zero_motion = 0;

    if (all && bot >= 0 && first_index_on_motion_zero >= bot) {
      /*
      java要求在时间区间之内
      且能递推出list临时列表
      因此all必须为1,且bot为正整数

      -bot的原因
      去除基索引的影响
       */
      ssize_t diff_index = first_index_on_motion_zero - bot;
      // fiomz 在#2中未被引用
      // 因此未扩充宏定义
      if ((strict_mode ? 0 <= diff_index && diff_index <= 1 : 1) &&
          motion_zero_count >= 2) {
        check_zero_motion = 1;
      } else {
        check_zero_motion = 0;
      }
    }

    if (MATCH_STEPS(all, bot, top, avg_mot, (uint64_t)(interval_milli / 1.5),
                    strict_mode ? FIND_SLEEP_MOTION_AVERAGE
                                : FIND_SLEEP_MOTION_AVERAGE_LAX,
                    <,
                    strict_mode ? FIND_SLEEP_STEPS_DIFF_THRESHOLD
                                : FIND_SLEEP_STEPS_DIFF_THRESHOLD_LAX) &&
        check_zero_motion) {
      start_index = (ssize_t)find_start;
      break;
    } else {
      find_start++;
    }
  }

  size_t find_end = index_end;
  while (find_end > index_start) {
    uint64_t end_time = (hr_data + find_end)->ts;
    uint64_t start_time = end_time - interval_milli;

    uint64_t sum_mot = 0;
    uint8_t all = 1;
    ssize_t bot = -1;
    size_t top = 0;
    double avg_mot;
    ssize_t first_index_on_motion_zero = -1;
    size_t _ = 0;

    ITER4_ALL(start_time, end_time,
              strict_mode ? FIND_WAKE_MOTION_THRESHOLD
                          : FIND_WAKE_MOTION_THRESHOLD_LAX,
              SLEEP_HR_AVG_THRESHOLD, all, bot, top, sum_mot, avg_mot,
              first_index_on_motion_zero, _, 10)
    NOTUSED(_);

    if (MATCH_STEPS(all, bot, top, avg_mot, (uint64_t)(interval_milli / 1.5),
                    FIND_WAKE_MOTION_AVERAGE, <=,
                    strict_mode ? FIND_WAKE_STEPS_DIFF_THRESHOLD
                                : FIND_WAKE_STEPS_DIFF_THRESHOLD_LAX)) {
      end_index = (ssize_t)find_end;
      break;
    } else {
      find_end--;
    }
  }

  if (-1 == start_index || -1 == end_index || start_index >= end_index ||
      !_enough((hr_data + start_index)->ts, (hr_data + end_index)->ts,
               MIN_JUDGMENT_SLEEP_TIME_20 * 60 * 1000)) {
    return 0;
  }

  *out_start = (size_t)start_index;
  *out_end = (size_t)end_index;
  return 1;
}

static uint8_t _sleep_staging_min_cond(const dist_action_t *dist,
                                       uint64_t sleep_start,
                                       const smp_hr_t *list, size_t count) {
  return (list + count - 1)->ts <=
         sleep_start + DEF_EVAL(dist, MIN_STAGING_TIME);
}

static uint8_t _staging_type_cond(uint32_t last_hr) {
  return last_hr <= N3_HR_THRESHOLD;
}

static void _seg_impl(const smp_hr_t *data, size_t count, size_t base,
                      uint64_t start_time, size_t interval, seg_info_t *info) {
  /*
  2 + (uint8_t)(interval / (60 * 1000)) / get_smp_interval();

  如果样本没有同时间的重复数据
  则可使用
  但重复数据的存在导致预估偏小
  */
  uint16_t tot = 0;

  for (size_t i = 0; i < count; i++) {
    const smp_hr_t *cur = data + i;

    if (cur->ts <= start_time + interval) {
      ++tot;
    } else {
      break;
    }
  }

  if (tot <= 1) {
    memset(info, 0x00, sizeof(*info));
    return;
  }

  uint32_t *hr_list = (uint32_t *)malloc(sizeof(uint32_t) * tot);
  uint32_t *hrv_list = (uint32_t *)malloc(sizeof(uint32_t) * tot);
  uint32_t *motion_list = (uint32_t *)malloc(sizeof(uint32_t) * tot);
  uint32_t *steps_list = (uint32_t *)malloc(sizeof(uint32_t) * tot);

  uint16_t actual = 0;

  for (size_t i = 0; i < count; i++) {
    const smp_hr_t *cur = data + i;

    if (cur->ts <= start_time + interval) {
      hr_list[actual] = cur->rate;
      hrv_list[actual] = cur->hrv;
      motion_list[actual] = cur->motion;
      steps_list[actual] = cur->steps;

      ++actual;
    } else {
      break;
    }
  }

  double hr_avg = avg_uint32_t(hr_list, tot);

  uint8_t all_hr_eq_avg = 1;
  uint32_t int_hr_avg = (uint32_t)hr_avg;
  for (uint16_t i = 0; i < tot; i++) {
    if (int_hr_avg != *(hr_list + i)) {
      all_hr_eq_avg = 0;
      break;
    }
  }

  uint8_t all_motion_zero = 1;
  for (uint16_t i = 0; i < tot; i++) {
    if (0 != *(motion_list + i)) {
      all_motion_zero = 0;
      break;
    }
  }

  if (0 && tot >= (15 / get_smp_interval()) && all_hr_eq_avg &&
      all_motion_zero) {
    memset(info, 0x00, sizeof(*info));
  } else {
    info->start_index = base;
    info->end_index = base + tot - 1;
    info->hr_avg = hr_avg;
    info->hr_sd = deviation_i32u(hr_list, tot);
    info->hrv_avg = avg_uint32_t(hrv_list, tot);
    info->hrv_sd = deviation_i32u(hrv_list, tot);
    info->motion_avg = avg_uint32_t(motion_list, tot);
    info->motion_sd = deviation_i32u(motion_list, tot);
    info->steps_sd = deviation_i32u(steps_list, tot);
    info->steps_diff = (int32_t)(steps_list[tot - 1] - steps_list[0]);
    info->valid = 1;
  }

  free(hr_list);
  free(hrv_list);
  free(motion_list);
  free(steps_list);
}

static void _v4_segment_calc(const smp_hr_t *hr_list, size_t count,
                             uint32_t time_minute, seg_info_t *result_list,
                             size_t *len_result) {
  size_t interval = time_minute * 60 * 1000;
  size_t j = 0;

  for (size_t i = 0; i < count; i++) {
    _seg_impl(hr_list + i, count - i, i, (hr_list + i)->ts, interval,
              result_list + (j++));
  }

  *len_result = j;
}

/*
@param src_list: 要求已排序
 */
static smp_hr_t *_pre_process(const smp_hr_t *src_list, size_t count,
                              size_t *dst_count) {
#define INTERVAL 60000

  *dst_count = 0;
  if (!src_list || !count)
    return NULL;

  /*
  按照目前5分钟的测量间隔
  以及手环10秒的单区间
  最大预估数量为30
   */
  uint16_t expect_len = 128;
  size_t temp_index = 0;
  smp_hr_t *temp_list = (smp_hr_t *)malloc(sizeof(smp_hr_t) * expect_len);

  smp_hr_t *target_list = (smp_hr_t *)malloc(sizeof(smp_hr_t) * count);
  size_t real_count = 0;

  for (size_t i = 0; i < count; i++) {
    const smp_hr_t *cur = src_list + i;

    if (temp_index && cur->ts >= temp_list->ts + INTERVAL) {
      smp_hr_avg(temp_list, temp_index, (target_list + real_count++));
      temp_index = 0;
    }

    memcpy(temp_list + (temp_index++), cur, sizeof(*cur));
  }

  if (temp_index) {
    smp_hr_avg(temp_list, temp_index, (target_list + real_count++));
  }

  free(temp_list);

  *dst_count = real_count;
  return target_list;
}

static inline uint8_t _first(const smp_hr_t *list, size_t count,
                             uint64_t cur_ts, size_t *out_index) {
  for (size_t i = 0; i < count; i++) {
    if ((list + i)->ts > cur_ts + CHECK_ACCELERATION_TIME) {
      *out_index = i;
      return 1;
    }
  }
  return 0;
}

static inline uint8_t _all_zero_mt(const smp_hr_t *list, size_t count) {
  for (size_t i = 0; i < count; i++) {
    if ((list + i)->motion >= 3)
      return 0;
  }
  return 1;
}

// 加速度传感器是否正常
static uint8_t _is_acc_functional(const smp_hr_t *list, size_t count) {
  if ((list + count - 1)->ts < list->ts + CHECK_ACCELERATION_TIME)
    return 1;

  uint64_t min_check_time = (list + count - 1)->ts - CHECK_ACCELERATION_TIME;
  uint32_t data_count = (uint32_t)(CHECK_ACCELERATION_TIME * 0.9 /
                                   (get_smp_interval() * 60 * 1000));

  for (size_t i = 0; i < count; i++) {
    uint64_t cur_ts = (list + i)->ts;
    if (cur_ts > min_check_time) {
      break;
    }

    size_t end_index;
    if (!_first(list, count, cur_ts, &end_index))
      continue;

    size_t span_size = end_index - i;
    if (span_size < data_count) {
      continue;
    }

    if (_all_zero_mt(list + i, span_size))
      return 0;
  }

  return 1;
}

static uint8_t _is_wear(const uint32_t *slist, size_t count) {
  /*
  kt使用subList函数
  其区间为 [x,y)
   */
  if (!slist)
    return 0;

  size_t begin, end;

  // 设置正确的区间
  if (count >= 5) {
    begin = 1;
    end = count - 1;
  } else {
    begin = 0;
    end = count;
  }

  uint32_t sum = 0;
  for (size_t i = begin; i < end; i++) {
    sum += *(slist + i);
  }

  uint32_t avg = (uint32_t)(sum * 1.0 / (double)(end - begin));

  uint8_t all = 1;

  for (size_t i = begin; i < end; i++) {
    if (*(slist + i) != avg) {
      all = 0;
      break;
    }
  }

  return !all;
}

static uint8_t _is_combine_sleep(const smp_hr_t *hr_list, const uint32_t *slist,
                                 size_t count) {
  NOTUSED(hr_list);
  return _is_wear(slist, count) && _is_acc_functional(hr_list, count);
}

static void _pre_release(smp_hr_t *p_list) {
  if (p_list)
    free(p_list);
}

static uint8_t _count4wake(void) {
  /*
  在java#1c82d0aa30186e29c56 提交中
  屏蔽了wake hr avg分支判断
  等同于此处 fast abort
   */
  return 0;
}

dist_action_t ver_sel = {
    .pre_proc = _pre_process,
    .pre_release = _pre_release,
    .count4wake = _count4wake,
    .proc_cond = _proc_append_cond,
    .sleep_range_cond = _find_sleep_range_cond,
    .find_sleep_start_end_impl = _find_sleep_start_end,
    .sleep_staging_min_cond = _sleep_staging_min_cond,
    .staging_type_cond = _staging_type_cond,
    .segment_calc = _v4_segment_calc,
    .is_wear = _is_combine_sleep,
    .is_acc_func = _is_acc_functional,
};

#endif //DIST_VER == 4

